home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkArgv.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  11KB  |  433 lines

  1. /*
  2.  * tkArgv.c --
  3.  *
  4.  *    This file contains a procedure that handles table-based
  5.  *    argv-argc parsing.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkArgv.c 1.19 95/06/25 15:06:14";
  15.  
  16. #include "tkPort.h"
  17. #include "tk.h"
  18.  
  19. /*
  20.  * Default table of argument descriptors.  These are normally available
  21.  * in every application.
  22.  */
  23.  
  24. static Tk_ArgvInfo defaultTable[] = {
  25.     {"-help",    TK_ARGV_HELP,    (char *) NULL,    (char *) NULL,
  26.     "Print summary of command-line options and abort"},
  27.     {NULL,    TK_ARGV_END,    (char *) NULL,    (char *) NULL,
  28.     (char *) NULL}
  29. };
  30.  
  31. /*
  32.  * Forward declarations for procedures defined in this file:
  33.  */
  34.  
  35. static void    PrintUsage _ANSI_ARGS_((Tcl_Interp *interp,
  36.             Tk_ArgvInfo *argTable, int flags));
  37.  
  38. /*
  39.  *----------------------------------------------------------------------
  40.  *
  41.  * Tk_ParseArgv --
  42.  *
  43.  *    Process an argv array according to a table of expected
  44.  *    command-line options.  See the manual page for more details.
  45.  *
  46.  * Results:
  47.  *    The return value is a standard Tcl return value.  If an
  48.  *    error occurs then an error message is left in interp->result.
  49.  *    Under normal conditions, both *argcPtr and *argv are modified
  50.  *    to return the arguments that couldn't be processed here (they
  51.  *    didn't match the option table, or followed an TK_ARGV_REST
  52.  *    argument).
  53.  *
  54.  * Side effects:
  55.  *    Variables may be modified, resources may be entered for tkwin,
  56.  *    or procedures may be called.  It all depends on the arguments
  57.  *    and their entries in argTable.  See the user documentation
  58.  *    for details.
  59.  *
  60.  *----------------------------------------------------------------------
  61.  */
  62.  
  63. int
  64. Tk_ParseArgv(interp, tkwin, argcPtr, argv, argTable, flags)
  65.     Tcl_Interp *interp;        /* Place to store error message. */
  66.     Tk_Window tkwin;        /* Window to use for setting Tk options.
  67.                  * NULL means ignore Tk option specs. */
  68.     int *argcPtr;        /* Number of arguments in argv.  Modified
  69.                  * to hold # args left in argv at end. */
  70.     char **argv;        /* Array of arguments.  Modified to hold
  71.                  * those that couldn't be processed here. */
  72.     Tk_ArgvInfo *argTable;    /* Array of option descriptions */
  73.     int flags;            /* Or'ed combination of various flag bits,
  74.                  * such as TK_ARGV_NO_DEFAULTS. */
  75. {
  76.     register Tk_ArgvInfo *infoPtr;
  77.                 /* Pointer to the current entry in the
  78.                  * table of argument descriptions. */
  79.     Tk_ArgvInfo *matchPtr;    /* Descriptor that matches current argument. */
  80.     char *curArg;        /* Current argument */
  81.     register char c;        /* Second character of current arg (used for
  82.                  * quick check for matching;  use 2nd char.
  83.                  * because first char. will almost always
  84.                  * be '-'). */
  85.     int srcIndex;        /* Location from which to read next argument
  86.                  * from argv. */
  87.     int dstIndex;        /* Index into argv to which next unused
  88.                  * argument should be copied (never greater
  89.                  * than srcIndex). */
  90.     int argc;            /* # arguments in argv still to process. */
  91.     size_t length;        /* Number of characters in current argument. */
  92.     int i;
  93.  
  94.     if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) {
  95.     srcIndex = dstIndex = 0;
  96.     argc = *argcPtr;
  97.     } else {
  98.     srcIndex = dstIndex = 1;
  99.     argc = *argcPtr-1;
  100.     }
  101.  
  102.     while (argc > 0) {
  103.     curArg = argv[srcIndex];
  104.     srcIndex++;
  105.     argc--;
  106.     length = strlen(curArg);
  107.     if (length > 0) {
  108.         c = curArg[1];
  109.     } else {
  110.         c = 0;
  111.     }
  112.  
  113.     /*
  114.      * Loop throught the argument descriptors searching for one with
  115.      * the matching key string.  If found, leave a pointer to it in
  116.      * matchPtr.
  117.      */
  118.  
  119.     matchPtr = NULL;
  120.     for (i = 0; i < 2; i++) {
  121.         if (i == 0) {
  122.         infoPtr = argTable;
  123.         } else {
  124.         infoPtr = defaultTable;
  125.         }
  126.         for (; (infoPtr != NULL) && (infoPtr->type != TK_ARGV_END);
  127.             infoPtr++) {
  128.          if (infoPtr->key == NULL) {
  129.              continue;
  130.          }
  131.          if ((infoPtr->key[1] != c)
  132.              || (strncmp(infoPtr->key, curArg, length) != 0)) {
  133.              continue;
  134.          }
  135.          if ((tkwin == NULL)
  136.              && ((infoPtr->type == TK_ARGV_CONST_OPTION)
  137.              || (infoPtr->type == TK_ARGV_OPTION_VALUE)
  138.              || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) {
  139.              continue;
  140.          }
  141.          if (infoPtr->key[length] == 0) {
  142.              matchPtr = infoPtr;
  143.              goto gotMatch;
  144.          }
  145.          if (flags & TK_ARGV_NO_ABBREV) {
  146.              continue;
  147.          }
  148.          if (matchPtr != NULL) {
  149.              Tcl_AppendResult(interp, "ambiguous option \"", curArg,
  150.                  "\"", (char *) NULL);
  151.              return TCL_ERROR;
  152.          }
  153.          matchPtr = infoPtr;
  154.         }
  155.     }
  156.     if (matchPtr == NULL) {
  157.  
  158.         /*
  159.          * Unrecognized argument.  Just copy it down, unless the caller
  160.          * prefers an error to be registered.
  161.          */
  162.  
  163.         if (flags & TK_ARGV_NO_LEFTOVERS) {
  164.         Tcl_AppendResult(interp, "unrecognized argument \"",
  165.             curArg, "\"", (char *) NULL);
  166.         return TCL_ERROR;
  167.         }
  168.         argv[dstIndex] = curArg;
  169.         dstIndex++;
  170.         continue;
  171.     }
  172.  
  173.     /*
  174.      * Take the appropriate action based on the option type
  175.      */
  176.  
  177.     gotMatch:
  178.     infoPtr = matchPtr;
  179.     switch (infoPtr->type) {
  180.         case TK_ARGV_CONSTANT:
  181.         *((int *) infoPtr->dst) = (int) infoPtr->src;
  182.         break;
  183.         case TK_ARGV_INT:
  184.         if (argc == 0) {
  185.             goto missingArg;
  186.         } else {
  187.             char *endPtr;
  188.  
  189.             *((int *) infoPtr->dst) =
  190.                 strtol(argv[srcIndex], &endPtr, 0);
  191.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  192.             Tcl_AppendResult(interp, "expected integer argument ",
  193.                 "for \"", infoPtr->key, "\" but got \"",
  194.                 argv[srcIndex], "\"", (char *) NULL);
  195.             return TCL_ERROR;
  196.             }
  197.             srcIndex++;
  198.             argc--;
  199.         }
  200.         break;
  201.         case TK_ARGV_STRING:
  202.         if (argc == 0) {
  203.             goto missingArg;
  204.         } else {
  205.             *((char **)infoPtr->dst) = argv[srcIndex];
  206.             srcIndex++;
  207.             argc--;
  208.         }
  209.         break;
  210.         case TK_ARGV_UID:
  211.         if (argc == 0) {
  212.             goto missingArg;
  213.         } else {
  214.             *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]);
  215.             srcIndex++;
  216.             argc--;
  217.         }
  218.         break;
  219.         case TK_ARGV_REST:
  220.         *((int *) infoPtr->dst) = dstIndex;
  221.         goto argsDone;
  222.         case TK_ARGV_FLOAT:
  223.         if (argc == 0) {
  224.             goto missingArg;
  225.         } else {
  226.             char *endPtr;
  227.  
  228.             *((double *) infoPtr->dst) =
  229.                 strtod(argv[srcIndex], &endPtr);
  230.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  231.             Tcl_AppendResult(interp, "expected floating-point ",
  232.                 "argument for \"", infoPtr->key,
  233.                 "\" but got \"", argv[srcIndex], "\"",
  234.                 (char *) NULL);
  235.             return TCL_ERROR;
  236.             }
  237.             srcIndex++;
  238.             argc--;
  239.         }
  240.         break;
  241.         case TK_ARGV_FUNC: {
  242.         int (*handlerProc)();
  243.  
  244.         handlerProc = (int (*)())infoPtr->src;
  245.         
  246.         if ((*handlerProc)(infoPtr->dst, infoPtr->key,
  247.             argv[srcIndex])) {
  248.             srcIndex += 1;
  249.             argc -= 1;
  250.         }
  251.         break;
  252.         }
  253.         case TK_ARGV_GENFUNC: {
  254.         int        (*handlerProc)();
  255.  
  256.         handlerProc = (int (*)())infoPtr->src;
  257.  
  258.         argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key,
  259.             argc, argv+srcIndex);
  260.         if (argc < 0) {
  261.             return TCL_ERROR;
  262.         }
  263.         break;
  264.         }
  265.         case TK_ARGV_HELP:
  266.         PrintUsage (interp, argTable, flags);
  267.         return TCL_ERROR;
  268.         case TK_ARGV_CONST_OPTION:
  269.         Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src,
  270.             TK_INTERACTIVE_PRIO);
  271.         break;
  272.         case TK_ARGV_OPTION_VALUE:
  273.         if (argc < 1) {
  274.             goto missingArg;
  275.         }
  276.         Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex],
  277.             TK_INTERACTIVE_PRIO);
  278.         srcIndex++;
  279.         argc--;
  280.         break;
  281.         case TK_ARGV_OPTION_NAME_VALUE:
  282.         if (argc < 2) {
  283.             Tcl_AppendResult(interp, "\"", curArg,
  284.                 "\" option requires two following arguments",
  285.                 (char *) NULL);
  286.             return TCL_ERROR;
  287.         }
  288.         Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1],
  289.             TK_INTERACTIVE_PRIO);
  290.         srcIndex += 2;
  291.         argc -= 2;
  292.         break;
  293.         default:
  294.         sprintf(interp->result, "bad argument type %d in Tk_ArgvInfo",
  295.             infoPtr->type);
  296.         return TCL_ERROR;
  297.     }
  298.     }
  299.  
  300.     /*
  301.      * If we broke out of the loop because of an OPT_REST argument,
  302.      * copy the remaining arguments down.
  303.      */
  304.  
  305.     argsDone:
  306.     while (argc) {
  307.     argv[dstIndex] = argv[srcIndex];
  308.     srcIndex++;
  309.     dstIndex++;
  310.     argc--;
  311.     }
  312.     argv[dstIndex] = (char *) NULL;
  313.     *argcPtr = dstIndex;
  314.     return TCL_OK;
  315.  
  316.     missingArg:
  317.     Tcl_AppendResult(interp, "\"", curArg,
  318.         "\" option requires an additional argument", (char *) NULL);
  319.     return TCL_ERROR;
  320. }
  321.  
  322. /*
  323.  *----------------------------------------------------------------------
  324.  *
  325.  * PrintUsage --
  326.  *
  327.  *    Generate a help string describing command-line options.
  328.  *
  329.  * Results:
  330.  *    Interp->result will be modified to hold a help string
  331.  *    describing all the options in argTable, plus all those
  332.  *    in the default table unless TK_ARGV_NO_DEFAULTS is
  333.  *    specified in flags.
  334.  *
  335.  * Side effects:
  336.  *    None.
  337.  *
  338.  *----------------------------------------------------------------------
  339.  */
  340.  
  341. static void
  342. PrintUsage(interp, argTable, flags)
  343.     Tcl_Interp *interp;        /* Place information in this interp's
  344.                  * result area. */
  345.     Tk_ArgvInfo *argTable;    /* Array of command-specific argument
  346.                  * descriptions. */
  347.     int flags;            /* If the TK_ARGV_NO_DEFAULTS bit is set
  348.                  * in this word, then don't generate
  349.                  * information for default options. */
  350. {
  351.     register Tk_ArgvInfo *infoPtr;
  352.     int width, i, numSpaces;
  353. #define NUM_SPACES 20
  354.     static char spaces[] = "                    ";
  355.     char tmp[30];
  356.  
  357.     /*
  358.      * First, compute the width of the widest option key, so that we
  359.      * can make everything line up.
  360.      */
  361.  
  362.     width = 4;
  363.     for (i = 0; i < 2; i++) {
  364.     for (infoPtr = i ? defaultTable : argTable;
  365.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  366.         int length;
  367.         if (infoPtr->key == NULL) {
  368.         continue;
  369.         }
  370.         length = strlen(infoPtr->key);
  371.         if (length > width) {
  372.         width = length;
  373.         }
  374.     }
  375.     }
  376.  
  377.     Tcl_AppendResult(interp, "Command-specific options:", (char *) NULL);
  378.     for (i = 0; ; i++) {
  379.     for (infoPtr = i ? defaultTable : argTable;
  380.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  381.         if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) {
  382.         Tcl_AppendResult(interp, "\n", infoPtr->help, (char *) NULL);
  383.         continue;
  384.         }
  385.         Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", (char *) NULL);
  386.         numSpaces = width + 1 - strlen(infoPtr->key);
  387.         while (numSpaces > 0) {
  388.         if (numSpaces >= NUM_SPACES) {
  389.             Tcl_AppendResult(interp, spaces, (char *) NULL);
  390.         } else {
  391.             Tcl_AppendResult(interp, spaces+NUM_SPACES-numSpaces,
  392.                 (char *) NULL);
  393.         }
  394.         numSpaces -= NUM_SPACES;
  395.         }
  396.         Tcl_AppendResult(interp, infoPtr->help, (char *) NULL);
  397.         switch (infoPtr->type) {
  398.         case TK_ARGV_INT: {
  399.             sprintf(tmp, "%d", *((int *) infoPtr->dst));
  400.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  401.                 tmp, (char *) NULL);
  402.             break;
  403.         }
  404.         case TK_ARGV_FLOAT: {
  405.             sprintf(tmp, "%g", *((double *) infoPtr->dst));
  406.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  407.                 tmp, (char *) NULL);
  408.             break;
  409.         }
  410.         case TK_ARGV_STRING: {
  411.             char *string;
  412.  
  413.             string = *((char **) infoPtr->dst);
  414.             if (string != NULL) {
  415.             Tcl_AppendResult(interp, "\n\t\tDefault value: \"",
  416.                 string, "\"", (char *) NULL);
  417.             }
  418.             break;
  419.         }
  420.         default: {
  421.             break;
  422.         }
  423.         }
  424.     }
  425.  
  426.     if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) {
  427.         break;
  428.     }
  429.     Tcl_AppendResult(interp, "\nGeneric options for all commands:",
  430.         (char *) NULL);
  431.     }
  432. }
  433.